using System;
using System.Diagnostics;
using System.IO;
using System.Management.Automation;
using Microsoft.Win32;
using Nivot.PowerShell.WindowsMobile.Rapi2;

namespace Nivot.PowerShell.WindowsMobile
{
    public class CEFile : CEObject, ICEInvokable
    {
        private ulong? _length;
        private readonly CEProxyController _ceProxy;

        internal CEFile(RemoteDevice device, string path) : base(device, path)
        {
            _ceProxy = new CEProxyController(device);
        }

        public override string Name
        {
            get { return Path.GetFileName(this.FullPath); }
        }

        public ulong Length
        {
            get
            {
                if (_length == null)
                {
                    _length = Device.GetFileSize(this.FullPath);
                }
                return (ulong) _length;
            }
        }

        protected CEProxyController CEProxy
        {
            get { return _ceProxy; }
        }

        #region ICEInvokable Members

        public virtual void InvokeItem(bool local)
        {
            if (local)
            {
                if ((this.Invokable & CEInvokable.Locally) == CEInvokable.Locally)
                {
                    InvokeLocally();
                }
                else
                {
                    throw new NotSupportedException("This object cannot be invoked locally.");
                }
            }
            else
            {
                if ((this.Invokable & CEInvokable.Remotely) == CEInvokable.Remotely)
                {
                    InvokeRemotely();
                }
                else
                {
                    throw new NotSupportedException("This object cannot be invoked remotely.");
                }
            }
        }

        protected virtual void InvokeRemotely()
        {
            CEProxy.ShellEx(this.FullPath);
        }

        protected virtual void InvokeLocally()
        {
            ShellEx();
        }

        private void ShellEx()
        {
            Provider.WriteVerbose(String.Format("Invoking {0} locally.", this.FullPath));

            string tempDir = Path.GetTempPath();
            string localFile = Path.Combine(tempDir, Path.GetFileName(this.FullPath));
            Device.CopyFileFromDevice(this.FullPath, localFile, true);

            var startInfo = new ProcessStartInfo { FileName = localFile, UseShellExecute = true };
            Process process = Process.Start(startInfo);
            if (process != null)
            {
                Provider.WriteVerbose(String.Format("Started process {0} (ID {1}).", localFile, process.Id));
            }
        }

        public virtual CEInvokable Invokable
        {
            get
            {                
                var invokable = CEInvokable.NotInvokable;
                try
                {
                    // determine if the local system (desktop) has an associated
                    // program with the remote file.
                    invokable = GetLocallyInvokable(invokable);

                    // check if the remote device has an associated program.
                    invokable = GetRemotelyInvokable(invokable);                    
                }
                catch (Exception ex)
                {
                    var record = new ErrorRecord(ex, "InvokableCheckFail", ErrorCategory.NotSpecified, null);
                    Provider.WriteError(record);
                }
                return invokable;
            }
        }

        private CEInvokable GetLocallyInvokable(CEInvokable invokable)
        {
            string ext = Path.GetExtension(this.FullPath);
            RegistryKey key = Registry.ClassesRoot.OpenSubKey(ext);
            if (key != null)
            {
                var desc = key.GetValue(String.Empty) as string;
                if (desc != null)
                {
                    RegistryKey descKey = Registry.ClassesRoot.OpenSubKey(
                        desc + @"\Shell\Open\Command");

                    if (descKey != null)
                    {
                        invokable |= CEInvokable.Locally;
                    }
                }
            }
            return invokable;
        }

        private CEInvokable GetRemotelyInvokable(CEInvokable invokable)
        {
            string ext = Path.GetExtension(this.FullPath);
            RemoteDevice.DeviceRegistryKey key = Device.DeviceRegistryClassesRoot.OpenSubKey(ext);
            if (key != null)
            {
                var desc = key.GetValue(String.Empty, null) as string;
                if (desc != null)
                {
                    RemoteDevice.DeviceRegistryKey descKey = Device.DeviceRegistryClassesRoot.OpenSubKey(
                        desc + @"\Shell\Open\Command");

                    if (descKey != null)
                    {
                        invokable |= CEInvokable.Remotely;
                    }
                }
            }
            return invokable;
        }

        #endregion
    }
}